#pragma once


#include <stdint.h>
#include <stdlib.h>

#include <type_traits>


#ifdef PI
    #undef PI
#endif

#ifdef HALF_PI
    #undef HALF_PI
#endif

#ifdef TWO_PI
    #undef TWO_PI
#endif

#ifdef DEG_TO_RAD
    #undef DEG_TO_RAD
#endif

#ifdef RAD_TO_DEG
    #undef RAD_TO_DEG
#endif

#ifdef EULER
    #undef EULER
#endif

#ifdef abs
    #undef abs
#endif


namespace arduino_basic {

    using FloatType = float;


    constexpr auto PI = static_cast<FloatType>(3.1415926535897932384626433832795);
    constexpr auto HALF_PI = static_cast<FloatType>(1.5707963267948966192313216916398);
    constexpr auto TWO_PI = static_cast<FloatType>(6.283185307179586476925286766559);
    constexpr auto DEG_TO_RAD = static_cast<FloatType>(0.017453292519943295769236907684886);
    constexpr auto RAD_TO_DEG = static_cast<FloatType>(57.295779513082320876798154814105);
    constexpr auto EULER = static_cast<FloatType>(2.718281828459045235360287471352);


    constexpr auto abs(auto x) {
        return x > 0 ? x : -x;
    }


    constexpr int64_t round(float x) {
        return static_cast<int64_t>(x >= 0 ? (x + 0.5) : (x - 0.5));
    }


    constexpr int64_t round(double x) {
        return static_cast<int64_t>(x >= 0 ? (x + 0.5) : (x - 0.5));
    }


    template <typename T, typename L>
    constexpr auto min(const T& a, const L& b) {
        return (b < a) ? b : a;
    }


    template <class T, class L>
    constexpr auto max(const T& a, const L& b) {
        return (a < b) ? b : a;
    }


    constexpr auto sq(auto x) {
        return x * x;
    }


    constexpr auto radians(auto deg) {
        return deg * DEG_TO_RAD;
    }


    constexpr auto degrees(auto rad) {
        return rad * RAD_TO_DEG;
    }


    inline void randomSeed(uint32_t seed) {
        if (seed != 0) {
#ifdef __AVR_ARCH__
            // AVR-GCC lib 里有和C 标准库不同的随机数实现。
            // 标准库随机数函数都用int，在AVR 上int 是int16_t，但底层计算使用的是int32_t
            // 所以用标准库函数就会损失精度
            srandom(seed);
#else
            srand(static_cast<unsigned int>(seed));
#endif
        }
    }


    /**
     * @brief 生成随机数，返回值范围: [0, max)
     * 
     * @param max 
     * @return int32_t 
     */
    inline int32_t random(int32_t max) {
        if (max == 0) {
            return 0;
        }
#ifdef __AVR_ARCH__
        auto r = ::random();
#else
        auto r = rand();
#endif
        return r % max;
    }


    /**
     * @brief 生成随机数，返回值范围: [min, max)
     * 
     * @param min 
     * @param max 
     * @return int32_t 
     */
    inline int32_t random(int32_t min, int32_t max) {
        if (min >= max) {
            return min;
        }
        
        auto diff = max - min;
        return random(diff) + min;
    }


    template <class V>
    constexpr V constrain(V x, V l, V h) {
        if (x < l)
            return l;
        else if (x > h)
            return h;
        else
            return x;
    }


    /**
     * @brief 等比例映射
     *
     * 如果原数据超过了设置的原值域，那么映射后的数据也将超出新值域，映射函数本身不做限幅处理。
     *
     * @tparam V 数据类型
     * @param x 待映射的原始数据
     * @param in_min 原值域下界
     * @param in_max 原值域上界
     * @param out_min 新值域下界
     * @param out_max 新值域上界
     * @return V 映射后的新值
     */
    template <typename V>
    constexpr V map(V x, V in_min, V in_max, V out_min, V out_max) {
        return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    }


    // 再添加一些方便使用的运算工具，函数命名改为Arduino 的camel 风格


    template <typename V, V IN_MIN, V IN_MAX, V OUT_MIN, V OUT_MAX>
    constexpr V map(V x) {
        return (x - IN_MIN) * (OUT_MAX - OUT_MIN) / (IN_MAX - IN_MIN) + OUT_MIN;
    }


    template <typename V>
    constexpr V absDelta(V a, V b) {
        if constexpr (std::is_unsigned_v<T>) {
            if (a > b) {
                return a - b;
            }
            else {
                return b - a;
            }
        }
        else {
            return abs(a - b);
        }
    }


    /**
     * @brief 将map 运算的中间结果缓存下来，省去一次除法，提高运算速度
     *
     * @tparam V
     */
    template <typename V>
    class ValueClamp {
       protected:
        V _limit_min = 0;
        V _limit_max = 0;
        V _mapping_min = 0;
        V _mapping_max = 0;

        V _k = 0;
        V _b = 0;

       public:
        constexpr ValueClamp(V limit_min, V limit_max) :
            _limit_min(limit_min), _limit_max(limit_max) {}

        constexpr ValueClamp(V limit_min, V limit_max, V mapping_min, V mapping_max) :
            _limit_min(limit_min),
            _limit_max(limit_max),
            _mapping_min(mapping_min),
            _mapping_max(mapping_max) {
            _k = (_mapping_max - _mapping_min) / (_limit_max - _limit_min);
            _b = _mapping_min - _limit_min * _k;
        }

        constexpr ValueClamp() {}

        V limitMin() const {
            return _limit_min;
        }

        V limitMax() const {
            return _limit_max;
        }

        void setLimit(V min, V max) {
            if (min > max) {
                _limit_min = max;
                _limit_max = min;
            }
            else {
                _limit_max = max;
                _limit_min = min;
            }
        }

        V limit(V value) const {
            return constrain(value, _limit_min, _limit_max);
        }

        V map(V value) const {
            return value * _k + _b;
        }

        V mappingMin() const {
            return _mapping_min;
        }

        V mappingMax() const {
            return _mapping_max;
        }

        void setMapping(V min, V max) {
            if (min > max) {
                _mapping_min = max;
                _mapping_max = min;
            }
            else {
                _mapping_min = min;
                _mapping_max = max;
            }

            _k = (_mapping_max - _mapping_min) / (_limit_max - _limit_min);
            _b = _mapping_min - _limit_min * _k;
        }
    };

}  // namespace arduino_basic